package org.kgduyjj.toolbox.builder;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.util.Hashtable;
import java.util.Objects;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.kgduyjj.toolbox.exception.SimpleToolboxException;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeWriter;

/**
 * QRCode 构建器
 * 
 * @author Kgduyjj
 * @since 2020-07-26
 * @version Alpha-1.42
 * 
 * @deprecated 这个QRCode构建工具存在一定问题，生成的QRCode无法正常使用，暂时弃用
 */
@Deprecated
public final class QrCodeBuilder {
	/** 默认字符编码格式 */
	private static final String DEFAULT_CHARSET = "UTF-8";
	/** 默认宽度 */
	private static final int DEFAULT_QRCODE_SIZE = 200;
	/** 默认格式 */
	private static final String DEFAULT_FORMAT = "png";
	/** 日志工具 */
	private static Logger logger = Logger.getLogger(QrCodeBuilder.class);
	/** 实例对象 */
	private static QrCodeBuilder instance = new QrCodeBuilder();

	/** QRcode 宽度 */
	protected int qrCodeSize;
	/** QRcode 默认格式 */
	protected String format;
	/** 编码配置参数 */
	protected Hashtable<EncodeHintType, Object> encodeHints;
	/** 解码配置参数 */
	protected Hashtable<DecodeHintType, Object> decodeHints;

	private QrCodeBuilder() {
	}

	public int getQrCodeSize() {
		return qrCodeSize;
	}

	private void setQrCodeSize(int qrCodeSize) {
		this.qrCodeSize = qrCodeSize;
	}

	public String getFormat() {
		return format;
	}

	private void setFormat(String format) {
		this.format = format;
	}

	public Hashtable<EncodeHintType, Object> getEncodeHints() {
		return encodeHints;
	}

	public Hashtable<DecodeHintType, Object> getDecodeHints() {
		return decodeHints;
	}

	/**
	 * 获取工具单例对象实例
	 * 
	 * @return
	 */
	public static QrCodeBuilder getInstance() {
		if (instance == null) {
			instance = new QrCodeBuilder();
		}

		return instance._init().charset(DEFAULT_CHARSET);
	}

	/**
	 * 获取工具单例对象实例
	 * 
	 * @return
	 */
	public static QrCodeBuilder getInstance(int qrCodeSize, String format) {
		return getInstance()._init(qrCodeSize, format);
	}

	/**
	 * 获取工具单例对象实例
	 * 
	 * @return
	 */
	public static QrCodeBuilder getInstance(int qrCodeSize) {
		return getInstance()._init(qrCodeSize, DEFAULT_FORMAT);
	}

	/**
	 * 初始化配置（使用默认参数）
	 * 
	 * @return
	 */
	private QrCodeBuilder _init() {
		return _init(DEFAULT_QRCODE_SIZE, DEFAULT_FORMAT);
	}

	/**
	 * 初始化参数（使用客户设定参数）
	 * 
	 * @param width
	 * @param height
	 * @param format
	 * @return
	 */
	private QrCodeBuilder _init(int qrCodeSize, String format) {
		Objects.requireNonNull(qrCodeSize, "qrCodeSize");
		setQrCodeSize(qrCodeSize);
		if (format == null || format.length() == 0) {
			setFormat(DEFAULT_FORMAT);
		} else {
			setFormat(format);
		}
		return this;
	}

	/**
	 * 配置编码hints参数
	 * 
	 * @param type
	 * @param parameter
	 */
	private QrCodeBuilder _encodeConfig(EncodeHintType type, Object parameter) {
		if (this.encodeHints == null) {
			this.encodeHints = new Hashtable<EncodeHintType, Object>();
		}
		this.encodeHints.put(type, parameter);
		return this;
	}

	/**
	 * 配置解码hints参数
	 * 
	 * @param type
	 * @param parameter
	 * @return
	 */
	private QrCodeBuilder _decodeConfig(DecodeHintType type, Object parameter) {
		if (this.decodeHints == null) {
			this.decodeHints = new Hashtable<DecodeHintType, Object>();
		}
		this.decodeHints.put(type, parameter);
		return this;
	}

	/**
	 * 设置字符格式
	 * 
	 * @param charset
	 * @return
	 */
	public QrCodeBuilder charset(String charset) {
		Objects.requireNonNull(charset, "charset");

		return _decodeConfig(DecodeHintType.CHARACTER_SET, DEFAULT_CHARSET)._encodeConfig(EncodeHintType.CHARACTER_SET,
				DEFAULT_CHARSET);
	}

	/**
	 * 生成二维码输出流 在jsp页面中直接展示时使用 无须保存 即生成即展示
	 * 
	 * @param response
	 * @param content
	 * @throws SimpleToolboxException
	 */
	public boolean writeQRCode(OutputStream outputStream, String content) throws SimpleToolboxException {
		Objects.requireNonNull(outputStream, "OutputStream");
		Objects.requireNonNull(content, "Content");
		try {
			QRCodeWriter qrCodeWriter = new QRCodeWriter();
			// 创建比特矩阵(位矩阵)的QR码编码的字符串
			BitMatrix byteMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, getQrCodeSize(), getQrCodeSize(),
					getEncodeHints());
			// 使BufferedImage勾画QRCode (matrixWidth 是行二维码像素点)
			int matrixWidth = byteMatrix.getWidth();
			BufferedImage image = new BufferedImage(matrixWidth - 200, matrixWidth - 200, BufferedImage.TYPE_INT_RGB);
			image.createGraphics();
			Graphics2D graphics = (Graphics2D) image.getGraphics();
			graphics.setColor(Color.WHITE);
			graphics.fillRect(0, 0, matrixWidth, matrixWidth);
			// 使用比特矩阵画并保存图像
			graphics.setColor(Color.BLACK);
			for (int i = 0; i < matrixWidth; i++) {
				for (int j = 0; j < matrixWidth; j++) {
					if (byteMatrix.get(i, j)) {
						graphics.fillRect(i - 100, j - 100, 1, 1);
					}
				}
			}
			return ImageIO.write(image, getFormat(), outputStream);
		} catch (WriterException | IOException e) {
			logger.error("QRCode输出异常", e);
			throw new SimpleToolboxException("QRCode输出异常", e);
		}
	}

	/**
	 * 根据文本生成QRCode
	 * 
	 * @param filePath
	 * @param fileName
	 * @param content
	 * @throws SimpleToolboxException
	 */
	public String generate(String filePath, String fileName, String content) throws SimpleToolboxException {
		try {
			String path = FileSystems.getDefault().getPath(filePath, fileName).toString();
			boolean isGen = writeQRCode(new FileOutputStream(new File(path)), content);
			if (!isGen) {
				throw new SimpleToolboxException("未成功生成QRCode");
			}
			return path;
		} catch (SimpleToolboxException e) {
			logger.error("生成二维码异常", e);
			throw e;
		} catch (Exception e) {
			logger.error("生成二维码异常", e);
			throw new SimpleToolboxException("生成二维码异常", e);
		}
	}

	/**
	 * 生成二维码输出流 在jsp页面中直接展示时使用 无须保存 即生成即展示
	 * 
	 * @param response
	 * @param content
	 * @throws SimpleToolboxException
	 */
	public void generate(HttpServletResponse response, String content) throws SimpleToolboxException {
		Objects.requireNonNull(content, "Content");
		try {
			writeQRCode(response.getOutputStream(), content);
		} catch (IOException e) {
			logger.error("QRCode输出异常", e);
			throw new SimpleToolboxException("QRCode输出异常", e);
		}
	}

	/**
	 * 根据文件路径解析QRCode图片
	 * 
	 * @param filePath
	 * @throws SimpleToolboxException
	 */
	public String readQRCode(InputStream inputStream) throws SimpleToolboxException {
		Objects.requireNonNull(inputStream, "InputStream");
		Result result = null;
		try {
			BufferedImage image = ImageIO.read(inputStream);
			LuminanceSource source = new BufferedImageLuminanceSource(image);
			BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
			result = new MultiFormatReader().decode(binaryBitmap, getDecodeHints());// 对图像进行解码
			logger.debug(result.getText());
		} catch (IOException | NotFoundException e) {
			logger.error("解析QRCode文件异常", e);
			throw new SimpleToolboxException("解析QRCode文件异常", e);
		}
		return (result == null ? null : result.getText());
	}

	/**
	 * 
	 * @param filePath
	 * @return
	 * @throws SimpleToolboxException
	 * @throws FileNotFoundException
	 */
	public String read(String filePath) throws SimpleToolboxException {
		try {
			return readQRCode(new FileInputStream(new File(filePath)));
		} catch (FileNotFoundException e) {
			throw new SimpleToolboxException("未找到QRCode文件", e);
		}
	}

}
